﻿
/**
------------------------------------------------------------
* NGUYEN SY BACH
* bit.mta@gmail.com
* Hoc Vien Ky Thuat Quan Su
------------------------------------------------------------
*** Chuong trinh bien doi anh kich thuoc lon
------------------------------------------------------------
*/


#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>

#include "cuda.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "nsb_helper.h"
#include "nsb_helper_readbmp.h"
#include "nsb_functions.h"


#ifdef __cplusplus
extern "C"
{
#endif

	void TransformWithGPU(BYTE *&outPtr, int u0, int v0,
		int outWidth, int outHeight, int channels, const double *g,
		const BYTE *inPtr, int x0, int y0,
		int inWidth, int inHeight, int mode,
		dim3 threadsPerBlock,
		dim3 blocksPerGrid);

	void TransformWithCPU(BYTE *&outPtr, int u0, int v0,
		int outWidth, int outHeight, int channels, const double *g,
		const BYTE *inPtr, int x0, int y0,
		int inWidth, int inHeight, int mode);

#ifdef __cplusplus
}
#endif




extern int testWithOpenCV(int argc, char *argv[]);

int main(int argc, char* argv[])
{
	testWithOpenCV(argc, argv);
	return 0;
	int i = 0, k = 0;
	int iRows = 512; //Số hàng pixel của một mẩu ảnh kết quả
	int iCols = 512; //Số pixel trên một hàng của một mẩu ảnh kết quả
	double f[6] = { 1, 0, 0, 0, 1, 0 }; // hàm biến đổi
	char processor[6] = "cpu"; // chế độ chạy CPU | GPU | OpenMP
	int mode = 0; // phương pháp tái chế mẫu NEARESTNEIGHBOR | BILINEAR | BICUBIC
	dim3 threadsPerBlocks(4, 4); // số threads 2D trên một block
	dim3 blocksPerGrid(4, 4); // số blocks 2D trên grid

#pragma region Đọc đối số dòng lệnh
	//---- Lấy các tham số biến đổi ----
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/f") == 0){
			if (i + 6 < argc)
			for (k = i + 1; k < i + 7; ++k)
				f[k - i - 1] = atof(argv[k]);
			else{
				help();
				return 1;
			}
			break;
		}
	}

	//---- Lấy phương pháp tái chế mẫu ----
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/m") == 0){
			if (i + 1 < argc){
				if (strcmp(strlwr(argv[i + 1]), "bilinear") == 0)
					mode = 1;
				else if (strcmp(strlwr(argv[i + 1]), "bicubic") == 0)
					mode = 2;
				else mode = 0;
			}
			else{
				help();
				return 1;
			}
			break;
		}
	}

	//---- Lấy chế độ sẽ chạy ----
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/p") == 0){
			if (i + 1 < argc){
				if (strcmp(strlwr(argv[i + 1]), "openmp") == 0)
					strcpy(processor, "openmp");
				else if (strcmp(strlwr(argv[i + 1]), "gpu") == 0)
					strcpy(processor, "gpu");
				else strcpy(processor, "cpu");
			}
			else{
				help();
				return 1;
			}
			break;
		}
	}

	//---- Mẩu ảnh sẽ trích để chạy trong một lần ----
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/s") == 0){
			if (i + 2 < argc){
				iRows = atoi(argv[i + 1]);
				iCols = atoi(argv[i + 2]);
			}
			else{
				help();
				return 1;
			}
			break;
		}
	}

	//---- Số lượng threads 2D trên một blocks ----
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/t") == 0){
			if (i + 2 < argc){
				threadsPerBlocks = dim3(
					atoi(argv[i + 1]), atoi(argv[i + 2]));
			}
			else{
				help();
				return 1;
			}
			break;
		}
	}

	//---- Số lượng blocks 2D trên grid
	for (i = 1; i < argc; ++i){
		if (strcmp(strlwr(argv[i]), "/b") == 0){
			if (i + 2 < argc){
				blocksPerGrid = dim3(
					atoi(argv[i + 1]), atoi(argv[i + 2]));
			}
			else{
				help();
				return 1;
			}
			break;
		}
	}

#pragma endregion


	//---- Đọc ảnh đầu vào ----
	FILE *filePtr = fopen(argv[1], "r"); // con trỏ đọc ảnh đầu vào
	HANDLE_NULL(filePtr);
	int inWidth = 0;	// chiều rộng của ảnh đầu vào
	int inHeight = 0; // chiều cao của ảnh đầu vào
	int depth = 0;  // độ sâu màu (byte)
	int channels = 0; // số kênh ảnh
	int rowLength = 0; // Số bytes trên một hàng ảnh
	int headerSize = 0; // kích thước phần header (bytes)
	bool success = true;
	success = DocPhanHeader(filePtr, inWidth, inHeight, depth, rowLength,
		headerSize);
	channels = depth >> 3;
	if (success == false){
		fprintf(stderr, "KHONG the doc header file");
		return 1;
	}

	//---- Xác định tạo độ và kích thước của ảnh kết quả ----
	Point2f topLeft(0, 0); //Tọa độ góc trên bên trái của ảnh kết quả
	Point2f bottomRight(0, 0); // Tạo độ góc dưới bên phải của ảnh kết quả
	GetBox(0, 0, inWidth, inHeight, f,
		topLeft.x, topLeft.y,
		bottomRight.x, bottomRight.y);
	int outHeight; // Chiều cao của ảnh kết quả
	int outWidth; // Chiều rộng của ảnh kết quả
	outWidth = (int)(ceil(bottomRight.x) - floor(topLeft.x));
	outHeight = (int)(ceil(bottomRight.y) - floor(topLeft.y));

	//---- Thực hiện biến đổi trên từng mẩu ảnh ----
	BYTE *inPtr = NULL; //Con trỏ đến vùng dữ liệu ảnh sẽ lấy ra để xử lý
	BYTE *outPtr = NULL; //Con trỏ vùng dữ liệu ảnh sau khi đã xử lý
	int rows = 0; // Số hàng pixel sẽ đọc
	int cols = 0; // Số pixel sẽ đọc trên một hàng
	double g[6]; // Tham số của hàm ánh xạ ngược
	Inv(f, g);
	Point2f subTopLeft(0, 0); // Tạo độ góc trên bên trái của mẩu ảnh gốc
	Point2f subBottomRight(0, 0); // Tạo độ góc dưới bên phải của mẩu ảnh gốc
	int subRows = 0; // Số hàng pixel của mẩu ảnh gốc
	int subCols = 0; // Số pixel trên một hàng cua mẩu ảnh gốc
	int all = int(ceil(outHeight*1.0 / iRows)*ceil(outWidth*1.0 / iCols));
	int count = 0;
	int mem_size_out = iRows*iCols*channels*sizeof(BYTE);
	outPtr = (BYTE*)malloc(mem_size_out);
	HANDLE_NULL(outPtr);

	if (strcmp(processor, "gpu") == 0){
		cudaEvent_t start, stop;
		float kernelTime = 0;
		for (int v0 = 0; v0 < outHeight; v0 += iRows){
			for (int u0 = 0; u0 < outWidth; u0 += iCols){
				rows = MIN(iRows, outHeight - v0);
				cols = MIN(iCols, outWidth - u0);
				GetBox(u0, v0, cols, rows, g,
					subTopLeft.x, subTopLeft.y,
					subBottomRight.x, subBottomRight.y);
				subCols = (int)(ceil(subBottomRight.x) - floor(subTopLeft.x));
				subRows = (int)(ceil(subBottomRight.y) - floor(subTopLeft.y));

				int mem_size = subRows*subCols*channels*sizeof(BYTE);
				if (!inPtr){
					inPtr = (BYTE*)malloc(mem_size);
					HANDLE_NULL(inPtr);
				}
				printf("\n--\nmem_size=%d mem_size_out=%d\n"
					"rows=%d cols=%d\n"
					"subRows=%d subCols=%d\n",
					mem_size, mem_size_out,
					rows, cols,
					subRows, subCols);

				DocMauAnh(filePtr, headerSize,
					(int)subTopLeft.y, (int)subTopLeft.x, rows, cols,
					channels, rowLength, inPtr);
				cudaEventCreate(&start);
				cudaEventRecord(start, 0);
				TransformWithGPU(outPtr, u0, v0, cols, rows, channels, g,
					inPtr, (int)subTopLeft.x, (int)subTopLeft.y,
					subCols, subRows, mode, threadsPerBlocks, blocksPerGrid);
				cudaEventCreate(&stop);
				cudaEventRecord(stop, 0);
				cudaEventSynchronize(stop);
				cudaEventElapsedTime(&kernelTime, start, stop);
				//queueData.push_back(BlockData(u0, v0, rows, cols, channels, outPtr));
				putchar(13);
				printf("\nDang thuc hien: %d/%d = %0.2f%%", ++count, all, count*100.0 / all);
			}
		}
		printf("\nThoi gian: %0.2fms", kernelTime);
	}
	else if (strcmp(processor, "openmp") == 0){
		printf("Chua cai dat\n");
	}
	else{
		int beginTime = clock();
		for (int v0 = 0; v0 < outHeight; v0 += iRows){
			for (int u0 = 0; u0 < outWidth; u0 += iCols){
				rows = MIN(iRows, outHeight - v0);
				cols = MIN(iCols, outWidth - u0);
				GetBox(u0, v0, cols, rows, g,
					subTopLeft.x, subTopLeft.y,
					subBottomRight.x, subBottomRight.y);
				subCols = (int)(ceil(subBottomRight.x) - floor(subTopLeft.x));
				subRows = (int)(ceil(subBottomRight.y) - floor(subTopLeft.y));
				if (!inPtr){
					inPtr = (BYTE*)malloc(subRows*subCols*channels*sizeof(BYTE));
					HANDLE_NULL(inPtr);
				}
				//printf("\n%d %d \t %d %d\n", subRows, subCols, rows, cols);

				DocMauAnh(filePtr, headerSize,
					(int)subTopLeft.y, (int)subTopLeft.x, rows, cols,
					channels, rowLength, inPtr);

				TransformWithCPU(outPtr, u0, v0, cols, rows, channels, g,
					inPtr, (int)subTopLeft.x, (int)subTopLeft.y,
					subCols, subRows, mode);
				//queueData.push_back(BlockData(u0, v0, rows, cols, channels, outPtr));
				putchar(13);
				printf("Dang thuc hien: %d/%d = %0.2f%%", ++count, all, count*100.0 / all);
			}
		}
		printf("\nThoi gian: %0.2fs", float(clock() - beginTime) / CLOCKS_PER_SEC);
	}


	free(inPtr);
	free(outPtr);
	inPtr = outPtr = NULL;
	printf("\nXong !\n");
	fclose(filePtr);
	return 0;
	return 0;
}